#define ZCORE_SOURCE
#include "RapidXMLNode.hpp"
#include "RapidXML.hpp"

using namespace rapidxml;
namespace zzz{
RapidXMLNode::RapidXMLNode(const RapidXMLNode &node):node_(node.node_),root_(node.root_)
{}

RapidXMLNode::RapidXMLNode(xml_node<> *node, const RapidXML *root):node_(node),root_(root)
{}

//self
bool RapidXMLNode::IsValid() const
{
  return node_!=NULL;
}

bool RapidXMLNode::SetName(const string &name)
{
  node_->name(node_->document()->allocate_string(name.c_str()));
  return true;
}

//parent
RapidXMLNode RapidXMLNode::GetParent()
{
  ZRCHECK_NOT_NULL(node_->parent());
  return RapidXMLNode(node_->parent(), root_);
}

//children
zuint RapidXMLNode::NodeNumber() const
{
  zuint x=0;
  for (const xml_node<> *pChild=node_->first_node(); pChild!=0; pChild=pChild->next_sibling())
    if (pChild->type()==rapidxml::node_element) x++;
  return x;
}

RapidXMLNode RapidXMLNode::GetNode(zuint i)
{
  zuint x=-1;
  for (xml_node<> *pChild=node_->first_node();pChild!=0;pChild=pChild->next_sibling())
  {
    if (pChild->type()==rapidxml::node_element) x++;
    if (x==i) return RapidXMLNode(pChild,root_);
  }
  ZLOGF<<"Node "<<i<<" does not exist!";
  return RapidXMLNode(NULL,root_);
}

RapidXMLNode RapidXMLNode::GetNode(const string &name)
{
  return GetFirstNode(name);
}

bool RapidXMLNode::HasNode(const string &name)
{
  for (xml_node<> *pChild=node_->first_node(); pChild!=0; pChild=pChild->next_sibling()) {
    if (pChild->type()==rapidxml::node_element && name == pChild->name()) return true;
  }
  return false;
}

RapidXMLNode RapidXMLNode::AppendNode(const string &name)
{
  xml_node<> *element=node_->document()->allocate_node(rapidxml::node_element, node_->document()->allocate_string(name.c_str()));
  node_->append_node(element);
  return RapidXMLNode(element,root_);
}

bool RapidXMLNode::RemoveNode(const string &name)
{
  xml_node<> *n=node_->first_node(name.c_str());
  if (n==NULL) return false;
  node_->remove_node(n);
  return true;
}

bool RapidXMLNode::RemoveNode(zuint i)
{
  zuint x=-1;
  for (xml_node<> *pChild=node_->first_node();pChild!=0;pChild=pChild->next_sibling())
  {
    if (pChild->type()==rapidxml::node_element) x++;
    if (x==i) {
      node_->remove_node(pChild);
      return true;
    }
  }
  return false;
}

//iteration
RapidXMLNode RapidXMLNode::GetFirstNode(const string &name)
{
  if (name.empty())
    return RapidXMLNode(node_->first_node(NULL),root_);
  return RapidXMLNode(node_->first_node(name.c_str()),root_);
}

RapidXMLNode RapidXMLNode::GetNextSibling(const string &name)
{
  if (name.empty())
    return RapidXMLNode(node_->next_sibling(NULL),root_);
  return RapidXMLNode(node_->next_sibling(name.c_str()),root_);
}

void RapidXMLNode::GotoNextSibling(const string &name)
{
  if (name.empty())
    node_ = node_->next_sibling(NULL);
  node_=node_->next_sibling(name.c_str());
}


void RapidXMLNode::operator++()
{
  GotoNextSibling();
}

//attribute
bool RapidXMLNode::HasAttribute(const string &name) const
{
  return node_->first_attribute(name.c_str())!=NULL;
}

const char *RapidXMLNode::GetAttribute(const string &name, const char *missing) const
{
  const xml_attribute<>* res=node_->first_attribute(name.c_str());
  return ((res!=NULL)?res->value():missing);
}

bool RapidXMLNode::SetAttribute(const string &name, const char *value) const
{
  xml_attribute<>* res=node_->first_attribute(name.c_str());
  if (res==NULL)
    node_->append_attribute(
      node_->document()->allocate_attribute(
        node_->document()->allocate_string(name.c_str()),
        node_->document()->allocate_string(value)));
  else
    res->value(node_->document()->allocate_string(value));
  return true;
}

bool RapidXMLNode::SetAttribute(const string &name, const string &value) const
{
  SetAttribute(name,value.c_str());
  return true;
}

bool RapidXMLNode::RemoveAttribute(const string &name)
{
  xml_attribute<>* res=node_->first_attribute(name.c_str());
  if (res) {
    node_->remove_attribute(res);
    return true;
  }
  return false;
}

//Text
const char *RapidXMLNode::GetText() const
{
  return node_->value();
}

bool RapidXMLNode::SetText(const char *v) const
{
  node_->value(node_->document()->allocate_string(v));
  return true;
}

bool RapidXMLNode::SetText(const string &v) const
{
  return SetText(v.c_str());
}

bool RapidXMLNode::RemoveText()
{
  node_->value(NULL);
  return true;
}

const char *RapidXMLNode::GetName() const
{
  return node_->name();
}
}
